﻿
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
using System.Runtime.Remoting.Channels.Tcp;
using System.Text;
using System.Threading;
using System.Runtime.Remoting.Activation;
using EBA.Dynamic;
using System.ComponentModel.Composition;
using System.Collections;
using System.Security.Principal;

namespace EBA.Framework
{
    /// <summary>
    /// 远程插件，支持动态安装，卸载和更新。
    /// </summary>
    public abstract class RemotingBundleContext : MarshalByRefObject, IBundleContext
    {

        /// <summary>
        /// 已经注册的服务实例列表
        /// </summary>
        protected Dictionary<Type, object> Services = new Dictionary<Type, object>();

        /// <summary>
        /// 构造函数
        /// </summary>
        public RemotingBundleContext()
        {
            //this.BundleLauncher = new ProcessBundleLauncher();
        }

        /// <summary>
        /// 名称
        /// </summary>
        public abstract string Name
        {
            get;
        }

        /// <summary>
        /// 对应插件
        /// </summary>
        public IBundle Bundle
        {
            get;
            set;
        }

        /// <summary>
        /// 在上下文中注册<typeparamref name="TService"/>类型的服务实例。
        /// </summary>
        /// <typeparam name="TService">服务类型</typeparam>
        /// <param name="service">服务实例</param>
        public virtual void RegisterService<TService>(TService service)
        {
            var serviceType = typeof(TService);
            this.Services[serviceType] = service;

            var remotingService = service.DynamicCastTo<TService, RemotingService>() as RemotingService;

            remotingService.Initialize(service);

            var serviceUri = RemotingUris.BuildServiceUri(this.Runtime, this.Bundle.Id, serviceType);
            RemotingServices.Marshal(remotingService, serviceUri, serviceType);

        }

        /// <summary>
        /// 在上下文中获取类型为<typeparamref name="TService"/>的已注册服务。
        /// </summary>
        /// <typeparam name="TService">服务类型</typeparam>
        /// <returns>已注册的服务实例</returns>
        public virtual TService GetService<TService>()
        {
            //   Debugger.Launch();
            var serviceType = typeof(TService);
            TService service = default(TService);

            if (this.Services.ContainsKey(serviceType))
            {
                service = (TService)this.Services[serviceType];
            }
            else
            {
                if (this.RemotingChannelName != null)
                {
                    var serviceUri = RemotingUris.BuildServiceUri(this.Runtime, this.Bundle.Id, serviceType);

                    var fullServiceUri = string.Format("ipc://{0}/{1}", this.RemotingChannelName, serviceUri);

                    service = (TService)Activator.GetObject(serviceType, fullServiceUri);
                }
            }

            if (service.Equals(null))
            {
                throw new ServiceUnregisteredException(serviceType);
            }
            else
            {
                return service;
            }
        }


        /// <summary>
        /// 共享通道
        /// </summary>
        protected virtual IChannel Channel { get; set; }

        /// <summary>
        /// 远程共享通道名称
        /// </summary>
        protected virtual string RemotingChannelName { get; set; }
        /// <summary>
        /// 对应的运行时名称
        /// </summary>
        protected virtual string Runtime { get; set; }

        /// <summary>
        /// 用于远程插件已经启动情况下的上下文初始化。
        /// </summary>
        /// <param name="runtime"></param>
        /// <param name="bundle"></param>
        public virtual void Initialize(string runtime, IBundle bundle) 
        {
            this.Runtime = runtime;
            this.RemotingChannelName = RemotingUris.BuildRuntimeChannelName(runtime, bundle.Id);
            this.Bundle = bundle;
        }

        /// <summary>
        /// 插件启动器
        /// </summary>
        protected virtual IBundleLauncher BundleLauncher { get; set; }
        /// <summary>
        /// 初始化上下文信息。包括注册跨域通道，启动远程插件，并获取远程插件的引用。
        /// </summary>
        /// <param name="runtime"></param>
        /// <param name="startupPath"></param>
        /// <param name="bundleId"></param>
        public virtual void Initialize<TBundle>(string runtime, string startupPath, string bundleId) where TBundle:IBundle
        {
            //generate dynamic remtoing proxy type
            //(default(TBundle)).DynamicCastTo<RemotingBundle>();

           // DynamicFactory.DynamicCastTo(default(TBundle), typeof(TBundle), typeof(RemotingBundle));

            this.Runtime = runtime;
            var runtimeChannelName = RemotingUris.BuildRuntimeChannelName(runtime,bundleId);
            var bundleChannelName = RemotingUris.BuildBundleChannelName(runtime, bundleId);

            this.RemotingChannelName = bundleChannelName;


            SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
            NTAccount account = sid.Translate(typeof(NTAccount)) as NTAccount;

            IDictionary props = new Hashtable();
            props["portName"] = runtimeChannelName;
            props["name"] = runtimeChannelName;
            props["exclusiveAddressUse"] = false;
            props["authorizedGroup"] = account.Value;
           

            this.Channel = ChannelServices.GetChannel(runtimeChannelName);

            if (this.Channel == null)
            {

                BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
                serverProvider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;

                this.Channel = new IpcChannel(props, null, serverProvider);
                try
                {
                    ChannelServices.RegisterChannel(this.Channel, false);
                }
                catch (RemotingException)//The channel has already been registered.
                {
                    this.Channel = ChannelServices.GetChannel(runtimeChannelName);
                }
            }

            var bundleContextUri = RemotingUris.BuildBundleContextUri(this.Runtime, bundleId);


            RemotingServices.Marshal(this, bundleContextUri, typeof(RemotingBundleContext));

            var syncEvent = new AutoResetEvent(false);

            var syncEventUri = RemotingUris.BuildRuntimeSyncEventUri(runtime, bundleId);

            RemotingServices.Marshal(syncEvent, syncEventUri, typeof(AutoResetEvent));

            this.BundleLauncher.Run(runtime, startupPath, bundleId);

            //syncEvent.WaitOne();
            syncEvent.WaitOne(TimeSpan.FromSeconds(10));



            var bundleUri = RemotingUris.BuildFullBundleUri(bundleChannelName, bundleId);

            this.Bundle = (TBundle)Activator.GetObject(typeof(TBundle), bundleUri);
        }

      


        #region Dispose
        private bool _Disposed = false;
        /// <summary>
        /// 析构函数
        /// </summary>
        ~RemotingBundleContext()
        {
            Dispose(false);
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        /// <param name="disposing"></param>
        protected void Dispose(bool disposing)
        {
            if (!_Disposed)
            {
                if (disposing)
                {
                    if (this.BundleLauncher != null)
                    {
                        // this.BundleProcess.Close();
                        this.BundleLauncher.Dispose();
                        this.BundleLauncher = null;
                    }

                    if (this.Channel != null)
                    {
                        try
                        {
                            ChannelServices.UnregisterChannel(this.Channel);
                        }
                        catch (Exception)
                        {
                        }
                        this.Channel = null;
                    }
                    GC.SuppressFinalize(this);
                }
                _Disposed = true;
            }
        }


        public override object InitializeLifetimeService()
        {
            return null;
        }
        #endregion
    }
}
